tushare 获取A股数据的方法seaborn.heatmap() 绘制相关性热力图在现代投资组合理论中,资产间的相关性是核心概念:
皮尔逊相关系数衡量两个变量线性相关的强度和方向:
\[ \rho_{X,Y} = \frac{\text{Cov}(X,Y)}{\sigma_X \sigma_Y} = \frac{E[(X-\mu_X)(Y-\mu_Y)]}{\sigma_X \sigma_Y} \]
| 相关系数范围 | 含义 | 投资启示 |
|---|---|---|
| \([0.7, 1]\) | 强正相关 | 同涨同跌,分散化效果差 |
| \([0.4, 0.7)\) | 中度正相关 | 有一定联动性 |
| \([-0.1, 0.4)\) | 弱正相关/无相关 | 适合组合分散化 |
| \([-0.7, -0.1)\) | 中度负相关 | 天然对冲效果 |
| \([-1, -0.7)\) | 强负相关 | 对冲效果极好 |
投资组合方差的完整公式:
\[ \sigma_p^2 = \sum_i \sum_j w_i w_j \sigma_i \sigma_j \rho_{ij} \]
相关系数只能衡量线性关系,存在重要局限:
# 注:该代码块存在缩进错误且使用tushare API(非用户token),渲染时无法执行
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import tushare as ts # 导入tushare模块
import pandas as pd # 导入Pandas数据分析库
import seaborn as sns # 导入Seaborn可视化库
import matplotlib.pyplot as plt # 导入Matplotlib绑图库
plt.rcParams['font.sans-serif']=['SimHei'] # 用黑体显示中文
ts.set_token("1b80258a92e8a547628c8e8477d0c4c590d485d17dfa98eea5a46db7") # 设置Tushare API访问令牌
pro=ts.pro_api() # 初始化Tushare Pro API接口
# 获取股票代码和名称的对应关系
stock_list = {'600030.SH': '中信证券', '601318.SH': '中国平安', '600036.SH': '招商银行', '600519.SH': '贵州茅台','601668.SH':'中国建筑'}
# 获取数据
data = {}
for stock_code, stock_name in stock_list.items(): # 循环遍历
df = pro.daily(ts_code=stock_code, start_date='20230101', end_date='20230301') # 通过API获取df的行情数据
data[stock_name] = df['close'].values.tolist() # 创建列表data[stock_name]
# 转换为DataFrame
df=pd.DataFrame.from_dict(data)
# 绘制热力图
sns.heatmap(df.corr(), annot=True, cmap='coolwarm')
plt.title('股价热力图') # 设置图表标题
plt.savefig("1.png") # 保存图形至文件pro.daily() 与 df.corr()pro.daily():获取日线行情
ts_code:股票代码(如 '600030.SH')start_date / end_date:起止日期open/high/low/close/vol 等字段的 DataFramedf.corr():计算相关系数矩阵
sns.heatmap()sns.heatmap(df.corr(), annot=True, cmap='coolwarm') 参数详解:
| 参数 | 含义 | 本例取值 |
|---|---|---|
data |
输入的二维数据(相关系数矩阵) | df.corr() |
annot |
是否在格子中标注数值 | True |
cmap |
颜色映射方案 | 'coolwarm'(蓝红渐变) |
center |
颜色中心值 | 可设为 0 |
square |
格子是否为正方形 | 可设为 True |
| 名称 | 效果 | 适用场景 |
|---|---|---|
coolwarm |
蓝→白→红 | 通用,正负对比清晰 |
RdYlGn |
红→黄→绿 | 金融领域常用 |
RdBu |
红→白→蓝 | 色盲友好 |
viridis |
紫→绿→黄 | 感知均匀,适合打印 |
YlOrRd |
黄→橙→红 | 只有正值时 |
建议:金融热力图优先使用 'RdYlGn' 或 'coolwarm',便于直观区分正负相关。
在计算相关性前,通常先将价格转换为收益率:
简单收益率:
\[ R_t = \frac{P_t - P_{t-1}}{P_{t-1}} \]
对数收益率:
\[ r_t = \ln\left(\frac{P_t}{P_{t-1}}\right) = \ln(P_t) - \ln(P_{t-1}) \]
df.pct_change() 和 np.log(df / df.shift(1))| 特性 | 简单收益率 | 对数收益率 |
|---|---|---|
| 公式 | \((P_t - P_{t-1}) / P_{t-1}\) | \(\ln(P_t / P_{t-1})\) |
| 可加性 | 多期不可直接相加 | 可加:\(r_{1 \to 3} = r_1 + r_2 + r_3\) |
| 对称性 | 不对称:+10% 和 -10% 不对等 | 对称 |
| 分布 | 偏态 | 更接近正态分布 |
| 应用 | 报告收益率 | 计算波动率、建模 |
相关性分析建议:使用对数收益率,因为更接近正态分布且具有时间可加性。
# 计算简单收益率
# pct_change(): (当前值 - 上期值) / 上期值
simple_returns = df_prices.pct_change().dropna()
# 计算对数收益率
# ln(P_t / P_{t-1}) = ln(P_t) - ln(P_{t-1})
log_returns = np.log(df_prices / df_prices.shift(1)).dropna()
# 输出收益率预览
print('简单收益率(前5行):')
print(simple_returns.head())
print(f'\n对数收益率(前5行):')
print(log_returns.head())
# 收益率统计对比
print(f'\n简单收益率统计:')
print(simple_returns.describe().round(4))
print(f'\n对数收益率统计:')
print(log_returns.describe().round(4))# 使用对数收益率计算相关性矩阵
# corr() 默认使用皮尔逊相关系数 (method='pearson')
corr_matrix = log_returns.corr()
# 输出相关性矩阵
print('相关性矩阵:')
print(corr_matrix.round(3))
print(f'\n矩阵形状: {corr_matrix.shape}')
# 验证对称性
print(f'\n矩阵对称性检验:')
print(f'是否对称: {np.allclose(corr_matrix, corr_matrix.T)}')
print(f'对角线元素(应全为1): {np.diag(corr_matrix)}')corr() 方法的三种计算方式:
| 参数 | 方法 | 特点 |
|---|---|---|
'pearson' |
皮尔逊(默认) | 衡量线性关系 |
'spearman' |
斯皮尔曼秩相关 | 对异常值稳健 |
'kendall' |
肯德尔 \(\tau\) 相关 | 适合小样本 |
检验原假设 \(H_0: \rho = 0\)(两变量无线性相关)
\[ t = \frac{r\sqrt{n-2}}{\sqrt{1-r^2}} \sim t_{n-2} \]
| 符号 | p 值条件 | 含义 |
|---|---|---|
*** |
\(p < 0.01\) | 极显著 |
** |
\(p < 0.05\) | 显著 |
* |
\(p < 0.10\) | 边缘显著 |
ns |
\(p \geq 0.10\) | 不显著 |
注意:统计显著 \(\neq\) 经济显著,即使 p 值很小,系数很低仍不具实际意义。
from scipy.stats import pearsonr # 皮尔逊检验
n = len(corr_matrix)
pairs = []
for i in range(n):
for j in range(i+1, n):
stock1 = corr_matrix.index[i]
stock2 = corr_matrix.columns[j]
corr = corr_matrix.iloc[i, j]
# pearsonr() 返回 (相关系数, p值)
_, p_value = pearsonr(
log_returns[stock1], log_returns[stock2]
)
pairs.append({
'股票1': stock1,
'股票2': stock2,
'相关系数': corr,
'P值': p_value,
'显著性': '***' if p_value < 0.01
else '**' if p_value < 0.05
else '*' if p_value < 0.1
else 'ns'
})
df_pairs = pd.DataFrame(pairs).sort_values(
'相关系数', ascending=False
)
print(df_pairs.to_string(index=False))import seaborn as sns
plt.figure(figsize=(10, 8))
sns.heatmap(
corr_matrix, # 相关系数矩阵
annot=True, # 显示数值
cmap='RdYlGn', # 红-黄-绿配色
center=0, # 颜色中心值为0
square=True, # 正方形格子
linewidths=0.5, # 格子边框
fmt='.3f', # 保留3位小数
cbar_kws={'label': '皮尔逊相关系数'},
vmin=-1, vmax=1 # 固定范围[-1,1]
)
plt.title('股票收益率相关性热力图', fontsize=14, pad=20)
plt.xlabel('股票', fontsize=12)
plt.ylabel('股票', fontsize=12)
plt.tight_layout()
plt.show()sns.clustermap() 在热力图基础上添加树状图,将相似股票自动聚在一起:
from scipy.cluster.hierarchy import linkage
linkage_matrix = linkage(corr_matrix, method='average')
g = sns.clustermap(
corr_matrix, annot=True, cmap='RdYlGn',
center=0, square=True, linewidths=0.5,
fmt='.3f', figsize=(10, 10),
row_linkage=linkage_matrix,
col_linkage=linkage_matrix,
tree_kws={'linewidths': 1.5}
)
g.fig.suptitle('股票相关性聚类分析', fontsize=14, y=0.98)
plt.show()距离定义:基于相关性转换 \(d = 1 - |\rho|\)
三种链接方法:
| 方法 | 原理 | 特点 |
|---|---|---|
| 单链接 | 类间最小距离 | 易产生链式效应 |
| 完全链接 | 类间最大距离 | 类内直径小 |
| 平均链接 | 类间平均距离 | 最常用,本例采用 |
金融应用:发现同行业股票、识别风格因子、地域因子分析
将相关性表示为网络,直观展示股票关联结构:
plt.figure(figsize=(10, 8))
pos = nx.spring_layout(G, k=0.5, seed=42)
degrees = dict(G.degree())
# 节点:大小与连接数成正比
nx.draw_networkx_nodes(
G, pos,
node_size=[v * 300 for v in degrees.values()],
node_color='steelblue', alpha=0.7
)
# 边:绿色=正相关,红色=负相关
weights = [G[u][v]['weight'] for u, v in G.edges()]
edge_colors = ['red' if w < 0 else 'green' for w in weights]
nx.draw_networkx_edges(
G, pos,
width=[abs(w) * 3 for w in weights],
edge_color=edge_colors, alpha=0.6
)
nx.draw_networkx_labels(G, pos, font_size=10)
plt.title(f'股票相关性网络图(阈值={threshold})')
plt.axis('off')
plt.show()相关系数不是恒定的,会随市场状态变化:
stock_a = '中信证券'
stock_b = '中国平安'
window = 20 # 20日滚动窗口(约1个月)
# 计算滚动相关性
rolling_corr = log_returns[stock_a].rolling(
window
).corr(log_returns[stock_b])
fig, axes = plt.subplots(2, 1, figsize=(14, 10))
# 子图1:滚动相关系数
axes[0].plot(rolling_corr.index, rolling_corr, linewidth=2)
axes[0].axhline(y=0, color='k', linestyle='--')
axes[0].axhline(y=0.5, color='r', linestyle='--', label='阈值0.5')
axes[0].axhline(y=-0.5, color='r', linestyle='--', label='阈值-0.5')
axes[0].set_title(f'{stock_a}与{stock_b}的滚动相关性')
axes[0].legend()
# 子图2:归一化价格走势
norm_prices = df_prices[[stock_a, stock_b]] / \
df_prices[[stock_a, stock_b]].iloc[0] * 100
axes[1].plot(norm_prices[stock_a], label=stock_a)
axes[1].plot(norm_prices[stock_b], label=stock_b)
axes[1].set_title('归一化价格走势(初始=100)')
axes[1].legend()
plt.tight_layout()
plt.show()市场状态对相关性的影响:
策略启示:
[商业大数据分析与应用]